using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.Concurrent;
using System.Threading.Tasks;
using System.Threading;
namespace ArtificialIntelligence
{
    public class AI
    {
        public Job jobSpecification;
        public List<Job> jobStates = new List<Job>();
        public List<Ability> abilities=new List<Ability>();
        public Boolean jobIsDone = false;
        public Action<Job> AssessJobFunction;
        public Func<List<Job>, List<Job>> Distinct;
        public int trimmedJobStatePopulation=100;
        public bool useParallel = true;
        public int maxWhileLoops = 10;
        
        public void Work(Job inJobSpecification, Action<Job> inAssessJobFunction)
        {
            AssessJobFunction = inAssessJobFunction;
            jobSpecification = inJobSpecification;
            jobStates.Clear();
            ConcurrentBag<Job> trimJobStates = new ConcurrentBag<Job>();
            ConcurrentBag<Job> gatheredJobStates = new ConcurrentBag<Job>();
            jobStates.Add(jobSpecification);
            int oldJobStateCount;
            AssessJobFunction(jobStates[0]);
            int whileCount = 0;
            int oldJobStateIndex = 0;
            int L;
            while (jobIsDone == false)
            {
                oldJobStateCount = jobStates.Count;
                trimJobStates = new ConcurrentBag<Job>();
                gatheredJobStates = new ConcurrentBag<Job>();
                for (L = 0; L < jobStates.Count; L++)
                {
                    trimJobStates.Add(jobStates[L]);
                }
                Action nest = () =>
                {
                    Job oldJobState;
                    int abilityIndex;
                    Job[] newJobStates;
                    oldJobState = trimJobStates.ElementAt(oldJobStateIndex);
                    if (oldJobState.workedOn == false)
                    {
                        oldJobState.workedOn = true;
                        for (abilityIndex = 0; abilityIndex < abilities.Count; abilityIndex++)
                        {
                            newJobStates = abilities[abilityIndex].WorkFunction(oldJobState).ToArray();
                            foreach (Job newJobState in newJobStates)
                            {
                                AssessJobFunction(newJobState);
                                newJobState.parent = oldJobState;
                                gatheredJobStates.Add(newJobState);
                            }
                        }
                    }
                };
                int uBound = jobStates.Count;
                if (useParallel == false)
                {
                    for (oldJobStateIndex = 0; oldJobStateIndex < uBound; oldJobStateIndex++)
                    {
                        nest();
                        if (jobIsDone == true)
                        {
                            break;
                        }
                    }
                }
                else
                {
                    ParallelOptions po = new ParallelOptions();
                    CancellationTokenSource cts = new CancellationTokenSource();
                    po.CancellationToken = cts.Token;
                    po.MaxDegreeOfParallelism = System.Environment.ProcessorCount;
                    System.Threading.Tasks.Parallel.For(0, uBound, po, (i) =>
                    {
                        oldJobStateIndex = i;
                        nest();
                        if (jobIsDone == true)
                        {
                            uBound = 0;
                        }
                    });
                }
                jobStates = trimJobStates.ToList();
                jobStates.AddRange(gatheredJobStates.ToList());
                var nonNulls =
                    from n in jobStates
                    where n != null
                    select n;
                jobStates = nonNulls.ToList();

                jobStates = Distinct(jobStates);
                RankJobStates();

                whileCount++;
                if (jobStates.Count > trimmedJobStatePopulation)
                {
                    jobStates = jobStates.GetRange(0, trimmedJobStatePopulation);
                }
                if (whileCount == maxWhileLoops)
                {
                    jobIsDone = true;
                }
                if (jobStates[0].approved == true)
                {
                    jobIsDone = true;
                }
                //GC.Collect();
            }
        }

        public void RankJobStates()
        {
            jobStates.Sort(RankJobStates);
        }

        public static int RankJobStates(Job jobState1, Job jobState2)
        {
            if (jobState1.assessment < jobState2.assessment)
            {
                return 1;
            }
            else if (jobState1.assessment > jobState2.assessment)
            {
                return -1;
            }
            return 0;
        }

        public Job BestJobState
        {
            get
            {
                return jobStates[0];
            }
        }
    }
}